/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package docking.widgets.list;

import java.awt.Color;
import java.awt.Component;
import java.util.function.Function;

import javax.swing.*;
import javax.swing.border.Border;

import docking.widgets.AbstractGCellRenderer;
import generic.theme.GColor;
import generic.theme.GThemeDefaults.Colors.Palette;

/**
 * Provides a common implementation of a list renderer, for use in both JList and JComboBox.
 * <p>
 * HTML rendering defaults to disabled.  See {@link #setHTMLRenderingEnabled(boolean)}.
 *
 * @param <E> the element-type this list models.
 */
public class GListCellRenderer<E> extends AbstractGCellRenderer implements ListCellRenderer<E> {

	private static final Color LIST_BACKGROUND_COLOR = new GColor("color.bg.list.row");

	/**
	 * Returns a new ListCellRenderer that maps the list's data instance to a string used in the cell.
	 * <p>
	 * Use this if you only need to provide a way to get the string value from the type being shown
	 * in the list.
	 *
	 * @param cellToTextMappingFunction a function that maps your custom type to a string value
	 * @return new GListCellRenderer instance
	 */
	public static <E> GListCellRenderer<E> createDefaultTextRenderer(
			Function<E, String> cellToTextMappingFunction) {
		return new GListCellRenderer<>() {
			@Override
			protected String getItemText(E value) {
				return cellToTextMappingFunction.apply(value);
			}
		};
	}

	/**
	 * Constructs a new GListCellRenderer.
	 */
	public GListCellRenderer() {

		// lists don't need alternation for rows, as they don't use long columnar data
		setShouldAlternateRowBackgroundColors(false);

		// Base our borders on those used by the list.  ComboBoxes do not change the list borders in
		// the Look and Feel.
		noFocusBorder = getBorder("List.noFocusBorder");
		focusBorder = getBorder("List.focusCellHighlightBorder");
	}

	private Border getBorder(String id) {
		Border border = UIManager.getBorder(id);
		if (border == null) {
			// A reasonable default value picked based on examining the existing LaFs
			border = BorderFactory.createEmptyBorder(2, 4, 2, 4);
		}
		return border;
	}

	// overridden to return the list-specific background color
	@Override
	protected Color getDefaultBackgroundColor() {
		return LIST_BACKGROUND_COLOR;
	}

	/**
	 * Return the cell renderer text
	 * @param value Cell object value
	 * @return A string interpretation of value; generated by calling value.toString()
	 */
	protected String getItemText(E value) {
		return value == null ? "" : value.toString();
	}

	@Override
	public Component getListCellRendererComponent(JList<? extends E> list, E value, int index,
			boolean isSelected, boolean hasFocus) {

		setText(getItemText(value));

		setHorizontalAlignment(
			value instanceof Number ? SwingConstants.RIGHT : SwingConstants.LEFT);
		ListModel<? extends E> model = list.getModel();
		configureFont(list, model, index);

		if (isSelected) {
			setForeground(list.getSelectionForeground());
			setBackground(list.getSelectionBackground());
		}
		else {
			setForegroundColor(list, model, value);

			JList.DropLocation dropLocation = list.getDropLocation();
			// @formatter:off
			boolean isDropRow = (dropLocation != null &&
								dropLocation.isInsert() &&
								dropLocation.getIndex() == index);
			// @formatter:on
			if (isDropRow) {
				setBackground(Palette.CYAN);
			}
			else {
				setBackground(getAlternatingBackgroundColor(list, index));
			}
		}

		setBorder(hasFocus ? focusBorder : noFocusBorder);

		return this;
	}

	protected void setForegroundColor(JList<? extends E> list, ListModel<? extends E> model,
			Object value) {
		setForeground(list.getForeground());
	}

	protected void configureFont(JList<? extends E> list, ListModel<? extends E> model, int index) {
		setFont(defaultFont);
	}
}
